/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package com.db4o.collections;
import java.io.*;
import java.util.*;
import com.db4o.activation.*;
import com.db4o.ta.*;
/**
* Transparent activatable Map implementation.
* Implements Map interface using two arrays to store keys and values.<br><br>
* When instantiated as a result of a query, all the internal members
* are NOT activated at all. When internal members are required to
* perform an operation, the instance transparently activates all
* the members.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore.implements
* @sharpen.rename ArrayDictionary4
* @sharpen.partial
*/
@decaf.Ignore(unlessCompatible=decaf.Platform.JDK15)
public class ArrayMap4<K, V> implements Map<K, V>, Serializable, Cloneable,
Activatable {
/**
* @sharpen.ignore
*/
private static final long serialVersionUID = 1L;
private K[] _keys;
private V[] _values;
private int _size;
private transient Activator _activator;
public ArrayMap4() {
this(16);
}
public ArrayMap4(int initialCapacity) {
initializeBackingArray(initialCapacity);
}
/**
* activate basic implementation.
*
* @see com.db4o.ta.Activatable
*/
public void activate(ActivationPurpose purpose) {
if (_activator != null) {
_activator.activate(purpose);
}
}
/**
* bind basic implementation.
*
* @see com.db4o.ta.Activatable
*/
public void bind(Activator activator) {
if (_activator == activator) {
return;
}
if (activator != null && _activator != null) {
throw new IllegalStateException();
}
_activator = activator;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*/
public void clear() {
activate(ActivationPurpose.WRITE);
_size = 0;
Arrays.fill(_keys, defaultKeyValue());
Arrays.fill(_values, defaultValue());
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public boolean containsKey(Object key) {
return containsKeyImpl((K) key);
}
private boolean containsKeyImpl(K key) {
activate(ActivationPurpose.READ);
return indexOfKey(key) != -1;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
public boolean containsValue(Object value) {
activate(ActivationPurpose.READ);
return indexOf(_values, ((V)value)) != -1;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public Set<Map.Entry<K, V>> entrySet() {
activate(ActivationPurpose.READ);
HashSet<Map.Entry<K, V>> set = new HashSet<Entry<K, V>>();
for (int i = 0; i < _size; i++) {
MapEntry4<K, V> entry = new MapEntry4<K, V>(keyAt(i), valueAt(i));
set.add(entry);
}
return set;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public V get(Object key) {
activate(ActivationPurpose.READ);
int index = indexOfKey(key);
return index == -1 ? null : valueAt(index);
}
private V valueAt(int index) {
return _values[index];
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public Set<K> keySet() {
activate(ActivationPurpose.READ);
HashSet<K> set = new HashSet<K>();
for (int i = 0; i < _size; i++) {
set.add(keyAt(i));
}
return set;
}
private K keyAt(int i) {
return _keys[i];
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public V put(K key, V value) {
activate(ActivationPurpose.WRITE);
return putInternal(key, value);
}
/**
* @sharpen.ignore
*/
private V putInternal(K key, V value) {
int index = indexOfKey(key);
if (index == -1) {
insert(key, value);
return null;
}
return replace(index, value);
}
/**
* @sharpen.ignore
*/
private int indexOfKey(Object key) {
return indexOf(_keys, key);
}
private V replace(int index, V value) {
V oldValue = valueAt(index);
_values[index] = value;
return oldValue;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
public void putAll(Map<? extends K, ? extends V> t) {
activate(ActivationPurpose.WRITE);
for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) {
putInternal(entry.getKey(), entry.getValue());
}
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
public V remove(Object key) {
activate(ActivationPurpose.READ);
int index = indexOfKey(key);
if (index == -1) {
return null;
}
return delete(index);
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*/
public int size() {
activate(ActivationPurpose.READ);
return _size;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.property
*/
public Collection<V> values() {
activate(ActivationPurpose.READ);
ArrayList<V> list = new ArrayList<V>();
for (int i = 0; i < _size; i++) {
list.add(valueAt(i));
}
return list;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
public ArrayMap4<K, V> clone() {
activate(ActivationPurpose.READ);
try {
ArrayMap4<K, V> mapClone = (ArrayMap4<K, V>) super.clone();
mapClone._activator = null;
mapClone._keys = _keys.clone();
mapClone._values = _values.clone();
return mapClone;
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Map)) {
return false;
}
Map<K, V> other = (Map<K, V>) obj;
if (size() != other.size()) {
return false;
}
Set<K> otherKeySet = other.keySet();
for (Map.Entry<K, V> entry : entrySet()) {
K key = entry.getKey();
if (!otherKeySet.contains(key)) {
return false;
}
V value = entry.getValue();
if (!(value == null ? other.get(key) == null : value.equals(other.get(key)))) {
return false;
}
}
return true;
}
/**
* java.util.Map implementation but transparently
* activates the members as required.
*
* @see java.util.Map
* @see com.db4o.ta.Activatable
*/
public int hashCode() {
int hashCode = 0;
for (Map.Entry<K, V> entry : entrySet()) {
hashCode += entry.hashCode();
}
return hashCode;
}
@SuppressWarnings("unchecked")
private void initializeBackingArray(int length) {
_keys = allocateKeyStorage(length);
_values = allocateValueStorage(length);
}
/**
* @sharpen.ignore
*/
private int indexOf(Object[] array, Object obj) {
int index = -1;
for (int i = 0; i < _size; i++) {
if (array[i] ==null ? obj == null : array[i].equals(obj)) {
index = i;
break;
}
}
return index;
}
private void insert(K key, V value) {
ensureCapacity();
_keys[_size] = key;
_values[_size] = value;
_size ++;
}
@SuppressWarnings("unchecked")
private void ensureCapacity() {
if (_size == _keys.length) {
int count = _keys.length * 2;
K[] newKeys = allocateKeyStorage(count);
V[] newValues = allocateValueStorage(count);
System.arraycopy(_keys, 0, newKeys, 0, _size);
System.arraycopy(_values, 0, newValues, 0, _size);
_keys = newKeys;
_values = newValues;
}
}
private V delete(int index) {
activate(ActivationPurpose.WRITE);
V value = valueAt(index);
for (int i = index; i < _size -1; i++) {
_keys[i] = _keys[i + 1];
_values[i] = _values[i + 1];
}
_size--;
_keys[_size] = defaultKeyValue();
_values[_size] = defaultValue();
return value;
}
/**
* @sharpen.ignore
*/
private K defaultKeyValue() {
return null;
}
/**
* @sharpen.ignore
*/
private V defaultValue() {
return null;
}
/**
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
private V[] allocateValueStorage(int count) {
return (V[]) new Object[count];
}
/**
* @sharpen.ignore
*/
@SuppressWarnings("unchecked")
private K[] allocateKeyStorage(int count) {
return (K[]) new Object[count];
}
}